package org.muninn.niagara.services;

import java.io.PrintWriter;

import javax.baja.collection.BICollection;
import javax.baja.collection.BITable;
import javax.baja.collection.Column;
import javax.baja.collection.TableCursor;
import javax.baja.history.BHistoryConfig;
import javax.baja.naming.BOrd;
import javax.baja.sys.Action;
import javax.baja.sys.BAbsTime;
import javax.baja.sys.BIcon;
import javax.baja.sys.BObject;
import javax.baja.sys.Context;
import javax.baja.sys.Flags;
import javax.baja.sys.Property;
import javax.baja.sys.Sys;
import javax.baja.sys.Type;

import com.tridium.bql.projection.BProjectionTable;

public class BCSVExportService extends BMuninnService
{
  /** Allow service to be executed periodically (set false for manual execution only) **/
  public static final Property enabled = newProperty(0, false);
  public void setEnabled(boolean v) { setBoolean(enabled, v); }
  public boolean getEnabled() { return getBoolean(enabled); }
  
  public static final Property startDate = newProperty(0, BAbsTime.make().prevDay());
  public void setStartDate(BAbsTime t) { set(startDate, t); }
  public BAbsTime getStartDate() { return (BAbsTime) get(startDate); }
  
  public static final Property endDate = newProperty(0, BAbsTime.make());
  public void setEndDate(BAbsTime t) { set(endDate, t); }
  public BAbsTime getEndDate() { return (BAbsTime) get(endDate); }
  
  /** Path to the CSV file */
  public static final Property filePath = newProperty(0, "C:\\Documents and Settings\\Administrator\\Desktop\\CSVs\\");
  public void setFilePath(String v) { setString(filePath, v); }
  public String getFilePath() { return getString(filePath); }
  
  private static BCSVExportService INSTANCE = null;
  
  /** Export information recorded in the 24 hours following the start time (disregards end time) */
  public static final Action exportStartDate = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void exportStartDate() { invoke(exportStartDate, null, null); }
  public void doExportStartDate() throws Exception { export(0); }
  
  /** Export information between the start and end times */
  public static final Action exportRange = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void exportRange() { invoke(exportRange, null, null); }
  public void doExportRange() throws Exception { export(1); }
  
  /** Export information recorded after the start time (disregards end time) */
  public static final Action exportFromStartToNow = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void exportFromStartToNow() { invoke(exportFromStartToNow, null, null); }
  public void doExportFromStartToNow() throws Exception { export(2); }
  
  /** Export information recorded in the 24 hours prior to the current time (disregards start and end times) */
  public static final Action exportYesterday = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void exportYesterday() { invoke(exportYesterday, null, null); }
  public void doExportYesterday() throws Exception { export(3); }
  
  private String[] master;
  
  public void changed(Property p, Context cx)
  {
    if (!isRunning()) return;
    
    if (p.equals(filePath))
    {
      String path = getFilePath();
      if (path.charAt(path.length() - 1) != '\\') setFilePath(path += "\\");
    }
    
    if (p.equals(startDate))
    {
      BAbsTime start = getStartDate();
      BAbsTime end = getEndDate();
      BAbsTime now = BAbsTime.make();
     
      if (start.isAfter(now))
        setStartDate(now.prevDay());
      
      if (start.isBefore(end.prevMonth()))
        setEndDate(start.nextMonth());
      
      if (start.isAfter(end))
        setStartDate(end.prevDay());
    }
    
    if (p.equals(endDate))
    {
      BAbsTime start = getStartDate();
      BAbsTime end = getEndDate();
      BAbsTime now = BAbsTime.make();
      
      if (end.isAfter(now))
        setEndDate(now);
      
      if (end.isAfter(start.nextMonth()))
        setStartDate(end.prevMonth());
      
      if (end.isBefore(start))
        setEndDate(start.nextDay());
    }
  }
  
  public void export(int x) throws Exception
  {
    if (!getEnabled()) return;
    
    System.out.println("Export started...");
    
    //lock in times
    BAbsTime start = getStartDate().toLocalTime();
    BAbsTime end = getEndDate().toLocalTime();
    BAbsTime now = BAbsTime.make();
    
    //get references to all hists
    BProjectionTable results = (BProjectionTable) BOrd.make("history:|bql:select * where isNull = false").resolve().get();
    BObject[] hists = results.list();
    master = new String[hists.length];
    
    for (int i = 0; i < hists.length; i++)
    { 
      //get name of target
      String id = ((BHistoryConfig) hists[i]).getId().toString();
      String name = id.substring(id.lastIndexOf("/") + 1);
      master[i] = name;
      System.out.print("Exporting " + name + "... ");
      
      //get history object
      BObject histTarg = BOrd.make("history:" + ((BHistoryConfig) hists[i]).getId()).get();
      BITable table = ((BICollection) histTarg).toTable();
      TableCursor cur = (TableCursor) table.cursor();
      Column[] cols = table.getColumns().list();
      
      switch (x)
      {
        case 0:
          export(name, cur, cols, start, start.nextDay());
          break;
        
        case 1:
          export(name, cur, cols, start, end);
          break;
        
        case 2:
          export(name, cur, cols, start, now);
          break;
        
        case 3: default:
          export(name, cur, cols, now.prevDay(), now);
      }
      
      //DO NOT DELETE THESE COMMENTED LINES
      //BExporter exporter = (BExporter) Sys.getType("file:ITableToCsv").getInstance();
      //ByteArrayOutputStream out = new ByteArrayOutputStream();
      //exporter.export(ExportOp.make(histTarg, out));
      
      //ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream(getFilePath() + name + "_" + date + ".csv", false));
      //oos.writeObject(out.toByteArray());
      //oos.close();
      
      System.out.println("Done");
    }
    
    System.out.print("Exporting master list... ");
    
    switch (x)
    {
      case 0: case 1: case 2:
        exportMaster(start);
        break;
      
      case 3: default:
        exportMaster(now.prevDay());
    }
    
    System.out.println("Done");
    
    System.out.println("Export finished");
  }
  
  public void export(String name, TableCursor cur, Column[] cols, BAbsTime start, BAbsTime end) throws Exception
  {
    //set up timestamp
    String date = makeDate(start);
    
    //set up writing to file
    PrintWriter writer = new PrintWriter(getFilePath() + name + "_" + date + ".csv", "UTF-8");
    
    //set up/write each line
    while (cur.next())
    {
      BAbsTime tempTime = (BAbsTime) cur.get(cols[0]);
      
      if (tempTime.isAfter(start) && tempTime.isBefore(end))
      {
        String fileLine = "";
        
        for (int j = 0; j < cols.length; j++)
          fileLine += (j == (cols.length - 1)) ? cur.get(cols[j]).toString() : (cur.get(cols[j]).toString() + ",");
          
        writer.println(fileLine);
      }
    }

    writer.close();
  }
  
  public void exportMaster(BAbsTime start) throws Exception
  {
    String date = makeDate(start);
    
    PrintWriter writer = new PrintWriter(getFilePath() + "MASTER_" + date + ".csv", "UTF-8");
    
    for (int i = 0; i < master.length; i++)
    {
      if (master[i].length() > 0)
        writer.println(master[i]);
    }
    
    writer.close();
  }
  
  public String makeDate(BAbsTime tm)
  {
    return Integer.toString(tm.getYear()).substring(2) +
           leftPad(Integer.toString(tm.getMonth().getMonthOfYear()), 2, '0') +
           leftPad(Integer.toString(tm.getDay()), 2, '0');
  }
  
  public String leftPad(String str, int lim, char ch)
  {
    while (str.length() < lim)
      str = ch + str;
    return str;
  }
  
  public BIcon getIcon() { return icon; }
  private static final BIcon icon = BIcon.make("local:|module://muninn/org/muninn/niagara/graphics/MuninnGroup.PNG");

  public static final Type TYPE = Sys.loadType(BCSVExportService.class);
  public Type[] getServiceTypes() { return new Type[] { TYPE }; }
  public Type getType() { return TYPE; }

  public final synchronized void serviceStarted() throws Exception { INSTANCE = this; }

  public final synchronized void serviceStopped() throws Exception { INSTANCE = null; }
  
  public static final BCSVExportService getInstance() { return INSTANCE; }
}
